/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package miscelania.almacenamiento;

/**
 *
 * @author UNSAAC73
 */
import java.util.Iterator;

import java.util.List;

import java.util.ArrayList;
import java.util.Scanner;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
//import org.taqque.modelo.test.Atributo;
//import org.taqque.modelo.test.Relacion;
/**
 * GestorIOTupla: Se encarga de entrada y salida de una tupla 
 * para el trabajo con paginas
 * @author hernan
 */
public class GestorIOTupla {
    /** relación a la que pertenecen las tuplas. */
    private Relacion relacion;//esquema de relacion   con la lista de atributos y sus tipos
    /** El nombre del archivo de la relación. */
    private String nombreArchivo;//archivo dond esta las tuplas correspondientes
    public int ContadorB=0;
    /**
     * Constructor de un nuevo gestor conociendo el esquema de la relación.
     * @param relacion, es el esquema de la relación.
     * @param nombre del archivo de la relación.
     */
    public GestorIOTupla(Relacion relacion, String nombreArchivo) {
        this.relacion = relacion;
        this.nombreArchivo = nombreArchivo;
    } // GestorIOTupla()
    /**
     * Escribir una tupla a un arreglo de byte -- esto se usará cuando
     * se gestione las tuplas en las páginas.
     * 
     * @param tupla la tupla a ser escrita.
     * @param bytes el arreglo de byte de la tupla a ser escrita.
     * @param inicio el desplazamiento inicial dentro del arreglo de bytes.
     * @return un nuevo desplazamiento en el arreglo de byte, si es -1 hay
     * problemas.
     */



    public int escribirTupla(Tupla tupla, byte[] bytes, int inicio) {
        // Escribir el id de la tupla pero en byte 
        byte[] b = Convert.toByte(tupla.getIdTupla().getNumero());
        System.arraycopy(b, 0, bytes, inicio, b.length); //el id ubicado en el array
        inicio += b.length; //el inicio ahora es actualizado segun en numero de bytes usados ´para el identificador
        // escribir los valores de la tupla a un arreglo de bytes
        b = null;
        int celda = 0;
        for (Atributo atr : relacion) {// en base a el esquema de relacion para verificar el tipo de los atributos
            inicio = volcadoDeCelda(atr.getTipo(), tupla, celda++, bytes, inicio);
        }
        return inicio;
    }
    
    public List<Par> leernTuplas(byte[] bytes,int inicio,int n){
        List<Par> tuplas = new ArrayList<Par>();
        Par<Tupla,Integer> par = new Par<Tupla, Integer>();
        par.segundo=inicio;
        for (int i=0;i<n;i++){
            //Tupla t = tuplas.get(i);
            par = leerTupla(bytes, par.segundo);
            tuplas.add(par);
        }
        return tuplas;
    }
     //metodo que controla el numero de bytes para el Gestor de paginas
    public int NroBytes() {
        //ContadorB = ContadorB + NumeroB;
        return ContadorB;
    }
    // escribirTupla()
    /**
     * Leer una tupla de un arreglo de bytes -- se utilizara cuando se gestione 
     * las tuplas en paginas.
     * @param bytes el arreglo de bytes.
     * @param inicio inicio del desplazamiento dentro del arreglo de bytes.
     * @return un objeto par consistiendo en la tupla leida y el nuevo 
     * desplazamiento en el arreglo de bytes.
     */
    public Par<Tupla, Integer> leerTupla(byte[] bytes, int inicio) {
        // leer el id de la tupla
        byte[] b = new byte[Convert.INT_SIZE];
        System.arraycopy(bytes, inicio, b, 0, b.length);
        int id = Convert.toInt(b);
        inicio += b.length;
        List<Comparable> valores = new ArrayList<Comparable>();

        int celda = 0;

        for (Iterator<Atributo> it = relacion.iterator();

                it.hasNext(); celda++) {

            Par<? extends Comparable, Integer> par = extraer(it.next().getTipo(), bytes, inicio);

            inicio = par.segundo;

            valores.add(par.primero);

        }

        Tupla t = new Tupla(new IdentificadorTupla(nombreArchivo, id), valores);

        return new Par<Tupla, Integer>(t, new Integer(inicio));

    } // leerTupla()
    /**
     * método base para volcar una celda de tupla a un arreglo de bytes.
     * @param tipo el tipo de la celda.
     * @param t la tupla.
     * @param s la celda de la tupla a ser volcada como arreglo de bytes.
     * @param bytes el arreglo de donde la celda será volcada.
     * @param inicio el inicio de desplazamiento.
     * @return el nuevo desplazamiento en el arreglo.
     */

    protected int volcadoDeCelda(Class<? extends Comparable> tipo, Tupla t, int c, byte[] bytes, int inicio) {
        if (tipo.equals(Character.class)) {
            byte[] b = Convert.toByte(t.asChar(c));
            System.arraycopy(b, 0, bytes, inicio, b.length);
            return inicio + b.length;
        } else if (tipo.equals(Byte.class)) {
            bytes[inicio] = t.asByte(c);
            return inicio + 1;
        } else if (tipo.equals(Short.class)) {
            byte[] b = Convert.toByte(t.asShort(c));
            System.arraycopy(b, 0, bytes, inicio, b.length);
            return inicio + b.length;

        } else if (tipo.equals(Integer.class)) {

            byte[] b = Convert.toByte(t.asInt(c));

            System.arraycopy(b, 0, bytes, inicio, b.length);

            return inicio + b.length;

        } else if (tipo.equals(Long.class)) {

            byte[] b = Convert.toByte(t.asLong(c));

            System.arraycopy(b, 0, bytes, inicio, b.length);

            return inicio + b.length;

        } else if (tipo.equals(Float.class)) {

            byte[] b = Convert.toByte(t.asFloat(c));

            System.arraycopy(b, 0, bytes, inicio, b.length);

            return inicio + b.length;

        } else if (tipo.equals(Double.class)) {

            byte[] b = Convert.toByte(t.asDouble(c));

            System.arraycopy(b, 0, bytes, inicio, b.length);

            return inicio + b.length;

        } else if (tipo.equals(String.class)) {

            String st = t.asString(c);

            int len = st.length();

            byte[] b = Convert.toByte(len);

            System.arraycopy(b, 0, bytes, inicio, b.length);

            inicio += b.length;

            b = Convert.toByte(st);

            System.arraycopy(b, 0, bytes, inicio, b.length);

            return inicio + b.length;

        } else {

            System.err.println("No soporta el tipo: " + tipo.getClass().getName());

            return -1;
        }

    } // volcadoDeCelda()
    /**
     * Método básico para leer una celda de un arreglo de byte.
     * @param tipo el tipo de la celda a leer.
     * @param bytes el arreglo de bytes como entrada.
     * @param inicio el desplazamiento inicial en el arreglo de bytes.
     * @return un par de (valor, desplazamiento) con el valor leido y el nuevo
     * desplazamiento en el arreglo de bytes.
     */

    protected Par<? extends Comparable, Integer> extraer(Class<? extends Comparable> tipo, byte[] bytes, int inicio) {

        try {
            if (tipo.equals(Character.class)) {
                byte[] b = new byte[Convert.CHAR_SIZE];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                return new Par<Character, Integer>(
                        new Character(Convert.toChar(b)),
                        inicio + b.length);
            } else if (tipo.equals(Byte.class)) {
                return new Par<Byte, Integer>(bytes[inicio],
                        inicio + 1);
            } else if (tipo.equals(Short.class)) {
                byte[] b = new byte[Convert.SHORT_SIZE];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                return new Par<Short, Integer>(new Short(Convert.toShort(b)),
                        inicio + b.length);
            } else if (tipo.equals(Integer.class)) {
                byte[] b = new byte[Convert.INT_SIZE];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                return new Par<Integer, Integer>(new Integer(Convert.toInt(b)),
                        inicio + b.length);
            } else if (tipo.equals(Long.class)) {
                byte[] b = new byte[Convert.LONG_SIZE];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                return new Par<Long, Integer>(new Long(Convert.toLong(b)),
                        inicio + b.length);
            } else if (tipo.equals(Float.class)) {
                byte[] b = new byte[Convert.FLOAT_SIZE];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                return new Par<Float, Integer>(new Float(Convert.toFloat(b)),
                        inicio + b.length);
            } else if (tipo.equals(Double.class)) {
                byte[] b = new byte[Convert.DOUBLE_SIZE];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                return new Par<Double, Integer>(
                        new Double(Convert.toDouble(b)),
                        inicio + b.length);
            } else if (tipo.equals(String.class)) {
                byte[] b = new byte[Convert.INT_SIZE];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                inicio += b.length;
                int stLength = Convert.toInt(b);
                b = new byte[2 * stLength];
                System.arraycopy(bytes, inicio, b, 0, b.length);
                String str = Convert.toString(b);
                return new Par<String, Integer>(str, inicio + b.length);
            } else {
                return null;
            }
        } catch (ArrayIndexOutOfBoundsException aiob) {
            return null;
        }
    } // extraer()
    /**
     * Calcular el tamaño de bytes de la tupla.
     * @param t la tupla.
     * @return el tamaño de byte de la tupla.

     */

    public int byteSize(Tupla t) {
        return byteSize(relacion, t);
    }
    /**
     * Calcular el tamaño en byte de una tupla dada su relación.      
     * @param rel la relación.
     * @param t la tupla.
     * @return el tamaño en byte de la tupla.
     */
    public static int byteSize(Relacion rel, Tupla t) {

        // una longitud para el id

        int size = Convert.INT_SIZE;

        int celda = 0;

        for (Atributo it : rel) {

            Class<?> tipo = it.getTipo();

            if (tipo.equals(Character.class)) {

                size += Convert.CHAR_SIZE;

            } else if (tipo.equals(Byte.class)) {

                size += 1;

            } else if (tipo.equals(Short.class)) {

                size += Convert.SHORT_SIZE;

            } else if (tipo.equals(Integer.class)) {

                size += Convert.INT_SIZE;

            } else if (tipo.equals(Long.class)) {

                size += Convert.LONG_SIZE;

            } else if (tipo.equals(Float.class)) {

                size += Convert.FLOAT_SIZE;

            } else if (tipo.equals(Double.class)) {

                size += Convert.DOUBLE_SIZE;

            } else if (tipo.equals(String.class)) {

                size += Convert.INT_SIZE + 2 * t.asString(celda).length();

            }




            celda++;

        }




        return size;

    } // byteSize()




    /**

     * Debug main.

     */
    public static byte[] Mostrar(ArrayList<Tupla> tu,GestorIOTupla man)
    {

       // java.io.RandomAccessFile raf = new java.io.RandomAccessFile("redes", "rw");
        byte[] bytes = new byte[1024];
        byte[] b= new byte[1];
            //man.escribirTupla(t, raf);
                 for (int j = 0; j < tu.size(); j++) {
                     if(j==0){

                        man.escribirTupla(tu.get(j), bytes, 0);
                           Par<Tupla, Integer> st = man.leerTupla(bytes, 0);
                        int o=st.segundo;
                         man.escribirTupla(tu.get(j), bytes, o);
                          System.out.println("mostrand..."+o);
                        b= new byte[o];
                     }
                     else{
                        
                          Par<Tupla, Integer> st = man.leerTupla(bytes, 0);
                        int o=st.segundo;
                         man.escribirTupla(tu.get(j), b, o);
                         System.arraycopy(b, 0, bytes, o, b.length);
                          System.out.println("mostrand..."+o);
                              st = man.leerTupla(bytes, 0);
                       o=st.segundo;
                         man.escribirTupla(tu.get(j), bytes, o);
                          System.out.println("mostrand..."+o);
                     }
                     System.out.println("mostrando bytes..."+bytes);
                 }
        System.out.println("reading tuple...");

            // t = man.leerTupla(bytes);

            Par<Tupla, Integer> st = man.leerTupla(bytes, 0);

            System.out.println(st);
        return bytes;

         //   raf.close();

         //   raf = new java.io.RandomAccessFile("redes", "r");



    }

    public static void main(String args[]) {





//        try {
//
//            List<Atributo> attrs = new ArrayList<Atributo>();
//
//            attrs.add(new Atributo("character", Character.class));
//
//            attrs.add(new Atributo("byte", Byte.class));
//
//            attrs.add(new Atributo("short", Short.class));
//
//            attrs.add(new Atributo("integer", Integer.class));
//
//            attrs.add(new Atributo("long", Long.class));
//
//            attrs.add(new Atributo("float", Float.class));
//
//            attrs.add(new Atributo("double", Double.class));
//
//            attrs.add(new Atributo("string", String.class));
//
//            Relacion rel = new Relacion(attrs);
//
//
//
//
//            List<Comparable> v = new ArrayList<Comparable>();
//
//            v.add(new Character('a'));
//
//            v.add(new Byte((byte) 26));
//
//            v.add(new Short((short) 312));
//
//            v.add(new Integer(2048));
//
//            v.add(new Long(34567));
//
//            v.add(new Float(12.3));
//
//            v.add(new Double(25.6));
//
//            v.add(new String("bla bla"));
//
//            Tupla t = new Tupla(new IdentificadorTupla("redes", 0), v);
//
//            GestorIOTupla man = new GestorIOTupla(rel, "redes");
//
//
//
//
//            java.io.RandomAccessFile raf = new java.io.RandomAccessFile("redes", "rw");
//
//
//
//
//            System.out.println("writing tuple..." + t);
//
//            byte[] bytes = new byte[1024];
//
//            //man.escribirTupla(t, raf);
//
//            man.escribirTupla(t, bytes, 0);
// System.out.println("rmostrando bytese..."+bytes);
//            raf.close();
//
//
//
//
//            raf = new java.io.RandomAccessFile("redes", "r");
//
//            System.out.println("reading tuple...");
//
//            // t = man.leerTupla(bytes);
//
//            Par<Tupla, Integer> st = man.leerTupla(bytes, 0);
//
//            System.out.println(st);
//
//            raf.close();
//
//        } catch (Exception e) {
//
//
//            System.err.println("Exception: " + e.getMessage());
//
//            e.printStackTrace(System.err);
//
//        }
       
             try {

            List<Atributo> attrs = new ArrayList<Atributo>();

            attrs.add(new Atributo("character", Character.class));

            attrs.add(new Atributo("byte", Byte.class));

            attrs.add(new Atributo("short", Short.class));

            attrs.add(new Atributo("integer", Integer.class));

            attrs.add(new Atributo("long", Long.class));

            attrs.add(new Atributo("float", Float.class));

            attrs.add(new Atributo("double", Double.class));

            attrs.add(new Atributo("string", String.class));

            Relacion rel = new Relacion(attrs);
System.out.println("Ingrese numero de  tuplas:");
             Scanner n = new Scanner(System.in);
             
        int m=n.nextInt();
 //       System.out.println("ddddd"+(float)n.nextDouble());
        List<Tupla> tu = new ArrayList<Tupla>(m);
        for (int i = 0; i < m; i++) {
             List<Comparable> v = new ArrayList<Comparable>();
             System.out.println("Char:");
             Scanner n1=new Scanner(System.in);
             v.add(new Character(n1.next().toCharArray()[0]));
            System.out.println("Byte:");
                      v.add(new Byte(n1.nextByte()));
            System.out.println("Short:");
            v.add(n1.nextShort());
            System.out.println("integer:");
            v.add(new Integer(n1.nextInt()));
            System.out.println("Long:");
            v.add(n1.nextLong());
            System.out.println("Float:");
            v.add(Float.parseFloat(n1.next()));
            System.out.println("Double:");
            v.add(Double.parseDouble(n1.next()));
            System.out.println("String:");
            v.add(n1.next());

            Tupla t = new Tupla(new IdentificadorTupla("redes", i), v);
         tu.add(t);
            
        }
       GestorIOTupla man = new GestorIOTupla(rel, "redes");

            java.io.RandomAccessFile raf = new java.io.RandomAccessFile("redes", "rw");

                 for (int k = 0; k < tu.size(); k++) {
                      System.out.println("writing tuple..." + tu.get(k));

                 }

            byte[] bytes = new byte[1024];

         //   man.escribirnTuplas(bytes,0,tu,tu.size());
            raf.close();

            System.out.println("Mostrar arreglo de bytes"+bytes);

            raf = new java.io.RandomAccessFile("redes", "r");
            System.out.println("reading tuple...");
            // t = man.leerTupla(bytes);

            List<Par> tuplas = man.leernTuplas(bytes,0,tu.size());
           
            for (int i = 0;i<tuplas.size();i++)
            {System.out.println(tuplas.get(i));}

            //System.out.println(st);
            raf.close();
        } catch (Exception e) {
            System.err.println("Exception: " + e.getMessage());
            e.printStackTrace(System.err);
        }
//            GestorIOTupla man = new GestorIOTupla(rel, "redes");
//
//            java.io.RandomAccessFile raf = new java.io.RandomAccessFile("redes", "rw");
//
//            Mostrar(tu, man);
//            byte[] bytes = new byte[1024];
//
//            //man.escribirTupla(t, raf);
//                 for (int j = 0; j < tu.size(); j++) {
//                     man.escribirTupla(tu.get(j), bytes, 0);
//                     System.out.println("rmostrando bytese..."+bytes);
//                 }
//            //man.escribirTupla(t, bytes, 0);
//
//            raf.close();
//
//            raf = new java.io.RandomAccessFile("redes", "r");
//
//            System.out.println("reading tuple...");
//
//            // t = man.leerTupla(bytes);
//
//            Par<Tupla, Integer> st = man.leerTupla(bytes, 0);
//
//            System.out.println(st);
//
//            raf.close();
//
//        } catch (Exception e) {
//
//
//            System.err.println("Exception: " + e.getMessage());
//
//            e.printStackTrace(System.err);
//
//        }
        
        

    } // main()

} // GestorIOTupla
